/*******************************************************************************
 * Copyright (c) 2011 Google, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Google, Inc. - initial API and implementation
 *******************************************************************************/
package org.eclipse.wb.swt;

import java.lang.reflect.Method;

import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerColumn;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Table;

/**
 * Helper for sorting {@link TableViewer} by one of its {@link TableViewerColumn}s.
 * <p>
 * Originally from http://wiki.eclipse.org/index.php/JFaceSnippets, Snippet040TableViewerSorting.
 * 
 * @author Tom Schindl <tom.schindl@bestsolution.at>
 * @author Konstantin Scheglov <Konstantin.Scheglov@gmail.com>
 */
public class TableViewerColumnSorter extends ViewerComparator {
	public static final int ASC = 1;
	public static final int NONE = 0;
	public static final int DESC = -1;
	////////////////////////////////////////////////////////////////////////////
	//
	// Instance fields
	//
	////////////////////////////////////////////////////////////////////////////
	private final TableViewerColumn m_column;
	private final TableViewer m_viewer;
	private final Table m_table;
	private int m_direction = NONE;
	////////////////////////////////////////////////////////////////////////////
	//
	// Constructor
	//
	////////////////////////////////////////////////////////////////////////////
	public TableViewerColumnSorter(TableViewerColumn column) {
		m_column = column;
		m_viewer = (TableViewer) column.getViewer();
		m_table = m_viewer.getTable();
		m_column.getColumn().addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				if (m_viewer.getComparator() != null) {
					if (m_viewer.getComparator() == TableViewerColumnSorter.this) {
						if (m_direction == ASC) {
							setSorter(DESC);
						} else if (m_direction == DESC) {
							setSorter(NONE);
						}
					} else {
						setSorter(ASC);
					}
				} else {
					setSorter(ASC);
				}
			}
		});
	}
	////////////////////////////////////////////////////////////////////////////
	//
	// Utils
	//
	////////////////////////////////////////////////////////////////////////////
	public void setSorter(int direction) {
		if (direction == NONE) {
			m_table.setSortColumn(null);
			m_table.setSortDirection(SWT.NONE);
			m_viewer.setComparator(null);
		} else {
			m_table.setSortColumn(m_column.getColumn());
			m_direction = direction;
			if (m_direction == ASC) {
				m_table.setSortDirection(SWT.DOWN);
			} else {
				m_table.setSortDirection(SWT.UP);
			}
			if (m_viewer.getComparator() == this) {
				m_viewer.refresh();
			} else {
				m_viewer.setComparator(this);
			}
		}
	}
	////////////////////////////////////////////////////////////////////////////
	//
	// ViewerComparator
	//
	////////////////////////////////////////////////////////////////////////////
	public int compare(Viewer viewer, Object e1, Object e2) {
		return m_direction * doCompare(viewer, e1, e2);
	}
	/**
	 * Compares to elements of viewer. By default tries to compare values extracted from these elements using
	 * {@link #getValue(Object)}, because usually you want to compare value of some attribute.
	 */
	@SuppressWarnings("unchecked")
	protected int doCompare(Viewer viewer, Object e1, Object e2) {
		Object o1 = getValue(e1);
		Object o2 = getValue(e2);
		if (o1 instanceof Comparable && o2 instanceof Comparable) {
			return ((Comparable) o1).compareTo(o2);
		}
		return 0;
	}
	/**
	 * 
	 * @return the value to compare in {@link #doCompare(Viewer, Object, Object)}. Be default tries to get it
	 *         from {@link EditingSupport}. May return <code>null</code>.
	 */
	protected Object getValue(Object o) {
		try {
			EditingSupport editingSupport;
			{
				Method getEditingMethod = ViewerColumn.class.getDeclaredMethod("getEditingSupport",
					new Class[]{});
				getEditingMethod.setAccessible(true);
				editingSupport = (EditingSupport) getEditingMethod.invoke(m_column, new Object[]{});
			}
			if (editingSupport != null) {
				Method getValueMethod = EditingSupport.class.getDeclaredMethod("getValue",
					new Class[]{Object.class});
				getValueMethod.setAccessible(true);
				return getValueMethod.invoke(editingSupport, new Object[]{o});
			}
		} catch (Throwable e) {
		}
		return null;
	}
}
